Hi all,
// string eval_ (string) will simply execute some DXL code and return an error string
print "--- Test 1 ---\n"
string s1 = eval_ "print \"Hello eval_!\""
if (s1 == "")
print "\nNo error occured (which is good!)\n"
else
print "\nWhoops! This should never happen\n"
// One can return a string from eval_ by using the return_ (string) function
print "\n--- Test 2 ---\n"
string s2 = eval_ "return_ \"Hello return_!\""
if (s2 == "Hello return_!")
print "return_ worked! (which is good!)\n\n"
else
print "Whoops! This should never happen\n\n"
// If your code is incorrect (=syntax error, code cannot be parsed) eval_ will return the string
// 'errors in eval_ string'
print "\n--- Test 3 ---\n"
string s3 = eval_ "incorrect dxl code"
if (s3 == "errors in eval_ string")
print "eval_ dumped the above error message and returned our error string (which is good!):\n" s3 "\n"
else
print "Whoops! This should never happen\n"
// if your code contains has a valid syntax, but produces a run time error, the DXL program inside eval_ will stop.
// The calling DXL program however will resume neither your eval_ dxl program, nor the calling program surpresses this error, then the calling DXL program will
// also stop.
print "\n--- Test 4 ---\n"
string s4 = eval_ "print (1/0); return_ \"This will not be returned!\""
if (s4 == "")
print "eval_ dumped the above error messages and stopped but we are still running (which is good!)\n\n"
else
print "Whoops! This should never happen\n"
// You cannot catch a runtime error from the outside of the eval_ context,
// nor can you suppress the dump of error messages to the console:
print "\n- Test 5 ---\n"
noError();
string s5 = eval_ "print (1/0);"
print "No error here: |" lastError() "|\n"
if (s5 == "")
print "the runtime error was not caught from the outside (which is good!)\n"
else
print "Whoops! This should never happen\n"
// So you need to catch runtime errors from the inside:
print "\n- Test 6 ---\n"
string s6 = eval_ "noError(); print (1/0); return_ lastError()"
if (s6[0:4] == "-R-E-")
print "Our eval_ returned our runtime errror (which is good!)\n"
else
print "Whoops! This should never happen\n"
So now that we know what we can do with eval_ how does this help us? Well it is important to understand a bit about the concepts of DXL contexts. A DXL context created by DOORS for every DXL program that is executed. DOORS executes multiple DXL scripts simultaneously at the same time. What?? Will some people ask, but DOORS is single threaded. Yes! But only one DXL program is running at a certain point of time. But there are a couple of conditions, where a DXL program is suspended to run another DXL program:
int i = 0; count = 0;
Column c = null
Module m = read("/Playground/My Module", true)
// clear the view
for c in m do count ++; // no leak here by the way!
for (i = 0; i < count; i++) delete column 0 // leak here, since column 0 allocates an object
c = insert column 0
attribute (c, "Object Identifier"); width(c, 150)
c = insert column 1
main (c); width(c, 400)
// Cool thing that DXL takes multi line strings, so you can just replace " by \" in some editor and paste the code to your DXL string sCode = " int i = 0; count = 0; Column c = null Module m = read(\"/Playground/My Module\", true) // clear the view for c in m do count ++; // no leak here by the way! for (i = 0; i < count; i++) delete column 0 // leak here, since column 0 allocates an object c = insert column 0 attribute (c, \"Object Identifier\"); width(c, 150) c = insert column 1 main (c); width(c, 400) " eval_ sCode
void printString(string sPar) {
// note that sCode is a multi line string, into which the
// string sPar is inserted: "..." sPar "..."
string sCode = "
string x = \"" sPar "\"
print x
"
print "Executing code:\n" sCode "\n"
eval_ sCode
}
printString "Hello eval_ function!"
int i = 10 int adInt = (addr_ (&i)) int // Note all non basic types are already references, so we do not need to create a reference // Note that if we pass the address of a variable (&i) in, we need to create a reference in the eval_ // code too: int &iEval // If we pass the value in (which is a reference by its nature) --> Module m // then we create a normal variable in the DXL context too: Module modVar Module m = current int adMod = (addr_ m) int string sCode = " int &iEval = addr_ " adInt " Module modEval = addr_ " adMod " print \"From eval: I = \" iEval \" and M = \" (fullName modEval) \"!\n\" " print sCode eval_ sCode
int &iEval = addr_ 182754240 Module modEval = addr_ 181847240 print "From eval: I = " iEval " and M = " (fullName modEval) "!\n"
int addr_ (int x) { return x }
char ch = 'A' int a = (addr_ ch) int print a // should give something like 65
Module m = current
Module modEval = m
modEval = read("MyModule")
Then you can see, that the modification of modEval does not result in a modification of m. If you do however
Buffer buf = create() Buffer b2 = buf b2 += "Hello"
Buffer buf = create() Buffer b2 = buf b2 = create() So the cool thing about the references and memory addresses is, that two DXL programs can share the same variables, that means we can pass in each variable we like in the eval context. This always works, since we can be sure, that the variables of the main program stay in memory, while the eval_ executes and do not change. Unfortunately this is not true the other way around. So if you create something in the eval_ context, and you want to pass it out, then this will eventually fail, since all allocated objects that were created in the eval_ context will be destroyed after the eval_ code ends. So this code, does not do what you want:
Skip sk = create()
string sCode = "
Skip skEval = addr_ " ((addr_ sk) int) "
string sEval = \"A String in eval_\"
sEval = sEval[0:7] // Comment this line out and everything will work
put (skEval, 0, sEval)
"
print "The code: " sCode "\n\n"
eval_ sCode
// Comment the below line in, if the sEval line is active, and things
// will start to break:
// int i, ar[100]; for (i = 0; i < 100; i++) ar[i] = 123
string s = ""
if (find(sk, 0, s)) {
print "Found string with index 0: |" s "|\n"
}
Buffer gBufPrintCode = create() // never reallocate this buffer
string sGlobalPrintCode = "
Buffer bufEval = addr_ " ((addr_ gBufPrintCode) int) "
print tempStringOf bufEval
"
void printString(string sPar) {
gBufPrintCode = sPar
eval_ sGlobalPrintCode
}
printString "Hello eval_ function!"
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
Mathias Mamsch - Tue Feb 19 13:50:41 EST 2013 |
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial
Like I said, "VooDoo".
Buffer b1, b2
b1 = create()
b1 += "after 'b1 = create()'"
b2 = b1
b2 += " after 'b2 = b1'" // this ends up in b1
b2 = create()
b2 += "after 'b2 = create()'" // this is in the new b2
print "\t" b1 "\n"
print "\t" b2 "\n"
/* Output:
after 'b1 = create()' after 'b2 = b1'
after 'b2 = create()'
*/
Module m1 = current
Module m2 = m1
Module &m3 = m1
print "\t" name(m1) "\t<">\t" name(m3) "\n"
m2 = read("Color Test", false)
print "\t" name(m1) "\t<">\t" name(m3) "\n"
m3 = read("Color Test", false)
print "\t" name(m1) "\t<">\t" name(m3) "\n"
/* Output:
AttrLayoutTests <AttrLayoutTests> AttrLayoutTests
AttrLayoutTests <Color Test> AttrLayoutTests
Color Test <Color Test> Color Test
*/
|
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial Thanks for taking the time to write that.
I think you are saying this:
Try to avoid multiple calls to eval_ because the code must be parsed for each call.
|
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial SystemAdmin - Wed Feb 20 04:05:21 EST 2013 Thanks for taking the time to write that.
I think you are saying this:
Try to avoid multiple calls to eval_ because the code must be parsed for each call.
If your code runs for one millisecond seconds, and the eval_ adds another millisecond to the evaluation, you have a 100% slowdown. If your code runs 20 ms and eval_ adds one ms, then you only have a slowdown of 5%. eval_ is very fast, so its not problem to call it often. But it might be more efficient, to create a whole view with eval_ than to create one single column and call this function 20 times. Regards, Mathias Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS |
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial Mathias Mamsch - Wed Feb 20 06:46:54 EST 2013 Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
By the way, if you want to avoid having to copy the contents of a buffer, that you pass to eval ( which can be relevant if you pass megabytes of data), then you can use can pass a reference to the eval_ by using a pointer. The problem is you need a static reassignable reference (=pointer) gBufPrintCode and put its static address to the eval code. Unfortunately you have to dereference twice in the eval code. Not nice, but fits its purpose ;-)
Buffer *gBufPrintCode
string sGlobalPrintCode = "
Buffer **bufEval = addr_ " ((addr_ (&gBufPrintCode)) int)"
Buffer &buf = **bufEval
print tempStringOf (buf)
"
void printString(Buffer &sPar) {
gBufPrintCode = &sPar
eval_ sGlobalPrintCode
}
Buffer buf = create(); buf = "Hello eval_ function!"
printString buf
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
|
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial Mathias Mamsch - Wed Feb 20 07:00:52 EST 2013
By the way, if you want to avoid having to copy the contents of a buffer, that you pass to eval ( which can be relevant if you pass megabytes of data), then you can use can pass a reference to the eval_ by using a pointer. The problem is you need a static reassignable reference (=pointer) gBufPrintCode and put its static address to the eval code. Unfortunately you have to dereference twice in the eval code. Not nice, but fits its purpose ;-)
Buffer *gBufPrintCode
string sGlobalPrintCode = "
Buffer **bufEval = addr_ " ((addr_ (&gBufPrintCode)) int)"
Buffer &buf = **bufEval
print tempStringOf (buf)
"
void printString(Buffer &sPar) {
gBufPrintCode = &sPar
eval_ sGlobalPrintCode
}
Buffer buf = create(); buf = "Hello eval_ function!"
printString buf
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
Hello Mathias #include <CommonWindowsRootPath.inc>; #include <library/evalCode/GlobalDataForEval_.inc>; #include <library/evalCode/createEval_String.inc>;
#include <CommonWindowsRootPath.inc>; #include <library/evalCode/GlobalDataForEval_.inc>; #include <library/evalCode/createEval_String.inc>; static string sFileEvalString = windowsRoot "library/evalCode/createEval_String.inc"; static string sFileLogWithoutLeak = windowsRoot "library/evalCode/logWithoutLeak.inc"; bufForEval_ = sFileEvalString; static string sCreateEvalString = createEval_String(bufForEval_); bufForEval_ = sFileLogWithoutLeak; static string sLogWithoutLeak = eval_(sCreateEvalString);
So the createEval_String()-Routine creates itself a string which can be used by eval_() and in the next step, the new string converts an other script to an eval_()-String. So you do not have and administration taks to perform, you write and develope your script and if you use in in the program, the script itself does the conversion.
pragma encoding, "UTF-8"
string createEval_String(Buffer bufLog) { // HEADERLINE
#include <ZF_PKW/library/common/Strings.inc>;
// ENDOFINCLUDES
string sData = stringOf(bufLog);
Skip skpWork = SplitString(readFile(sData), "\n");
int iOffset = null;
int iLength = null;
bufLog = "";
string sLine;
bool bDone = false;
for sLine in skpWork do {
if (bDone) { continue }
if (findPlainText(sLine,"HEADER" "LINE", iOffset, iLength, true)) {
// Skip this line ...
continue;
}
if (findPlainText(sLine, "END" "OF" "INCLUDES", iOffset, iLength, true)) {
string sH = "Buffer bufLog = addr_(" ((addr_ bufLog) int) ");"
bufLog += sH;
continue;
}
if (findPlainText(sLine, "END" "OF" "CODE", iOffset, iLength, true)) {
string sH = "return_(sResultString);"
bufLog += sH;
bDone = true;
continue;
}
bufLog += sLine "\n";
}
delete(skpWork);
string sResultString = stringOf(bufLog)
return sResultString; // ENDOFCODE
}
#include <library/evalCode/createEval_String.inc>;
Then you use the routine directly and of course, you have the leaks. Or you use:
|
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial SystemAdmin - Fri Feb 22 05:24:31 EST 2013
Hello Mathias #include <CommonWindowsRootPath.inc>; #include <library/evalCode/GlobalDataForEval_.inc>; #include <library/evalCode/createEval_String.inc>;
#include <CommonWindowsRootPath.inc>; #include <library/evalCode/GlobalDataForEval_.inc>; #include <library/evalCode/createEval_String.inc>; static string sFileEvalString = windowsRoot "library/evalCode/createEval_String.inc"; static string sFileLogWithoutLeak = windowsRoot "library/evalCode/logWithoutLeak.inc"; bufForEval_ = sFileEvalString; static string sCreateEvalString = createEval_String(bufForEval_); bufForEval_ = sFileLogWithoutLeak; static string sLogWithoutLeak = eval_(sCreateEvalString);
So the createEval_String()-Routine creates itself a string which can be used by eval_() and in the next step, the new string converts an other script to an eval_()-String. So you do not have and administration taks to perform, you write and develope your script and if you use in in the program, the script itself does the conversion.
pragma encoding, "UTF-8"
string createEval_String(Buffer bufLog) { // HEADERLINE
#include <ZF_PKW/library/common/Strings.inc>;
// ENDOFINCLUDES
string sData = stringOf(bufLog);
Skip skpWork = SplitString(readFile(sData), "\n");
int iOffset = null;
int iLength = null;
bufLog = "";
string sLine;
bool bDone = false;
for sLine in skpWork do {
if (bDone) { continue }
if (findPlainText(sLine,"HEADER" "LINE", iOffset, iLength, true)) {
// Skip this line ...
continue;
}
if (findPlainText(sLine, "END" "OF" "INCLUDES", iOffset, iLength, true)) {
string sH = "Buffer bufLog = addr_(" ((addr_ bufLog) int) ");"
bufLog += sH;
continue;
}
if (findPlainText(sLine, "END" "OF" "CODE", iOffset, iLength, true)) {
string sH = "return_(sResultString);"
bufLog += sH;
bDone = true;
continue;
}
bufLog += sLine "\n";
}
delete(skpWork);
string sResultString = stringOf(bufLog)
return sResultString; // ENDOFCODE
}
#include <library/evalCode/createEval_String.inc>;
Then you use the routine directly and of course, you have the leaks. Or you use:
|
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial Hi Mathias Can we use the eval_ function to load a module? If so, do you have a sample code to do that? Thanks, |
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial Hi Mathias, thank you for another brilliant lesson in DXL! (Actually several lessons just in this thread.) I have one addition regarding the behaviour of noError() and lastError() with eval_(): You can catch both syntax errors and runtime errors in the evaluated code. The trick is to have one noError() outside the eval and one within the eval; any errors that occur are propagated to the calling context.
bool try_eval(string code, string &outcome)
{
string errmsg;
noError(); // catch syntax errors in code
outcome = eval_("noError();" code); // catch runtime errors in code
errmsg = lastError();
if (null errmsg)
return true;
else {
outcome = errmsg;
return false;
}
}
// Test it...
string dxl_code[5];
string result;
// Syntax Error
dxl_code[0] = "int 2 = 2";
// Runtime Error
dxl_code[1] = "return_ fullName(null Module)";
// User defined error
dxl_code[2] = "error(\"My own error message!\")";
// Valid code with return value
dxl_code[3] = "int x = 1+2+3; x++;\n return_ x\"\"";
// Valid code without return value
dxl_code[4] = "print \"today is \"today\"\n\"";
int i;
for i in 0:sizeof(dxl_code)-1 do {
print "try_eval():\n";
print "|" dxl_code[i] "|\n";
print ((try_eval(dxl_code[i],result)) ? "Result" : "Error") ":\n>>"result"<<\n";
print "----\n\n";
}
You can see that the function error(string) only registers as "-I- DXL: execution halted"; you cannot pass out your own error message. But that's a minor niggle. |
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial MaltePlath - Fri Jun 06 08:14:35 EDT 2014 Hi Mathias, thank you for another brilliant lesson in DXL! (Actually several lessons just in this thread.) I have one addition regarding the behaviour of noError() and lastError() with eval_(): You can catch both syntax errors and runtime errors in the evaluated code. The trick is to have one noError() outside the eval and one within the eval; any errors that occur are propagated to the calling context.
bool try_eval(string code, string &outcome)
{
string errmsg;
noError(); // catch syntax errors in code
outcome = eval_("noError();" code); // catch runtime errors in code
errmsg = lastError();
if (null errmsg)
return true;
else {
outcome = errmsg;
return false;
}
}
// Test it...
string dxl_code[5];
string result;
// Syntax Error
dxl_code[0] = "int 2 = 2";
// Runtime Error
dxl_code[1] = "return_ fullName(null Module)";
// User defined error
dxl_code[2] = "error(\"My own error message!\")";
// Valid code with return value
dxl_code[3] = "int x = 1+2+3; x++;\n return_ x\"\"";
// Valid code without return value
dxl_code[4] = "print \"today is \"today\"\n\"";
int i;
for i in 0:sizeof(dxl_code)-1 do {
print "try_eval():\n";
print "|" dxl_code[i] "|\n";
print ((try_eval(dxl_code[i],result)) ? "Result" : "Error") ":\n>>"result"<<\n";
print "----\n\n";
}
You can see that the function error(string) only registers as "-I- DXL: execution halted"; you cannot pass out your own error message. But that's a minor niggle. You can also send the eval DXL code to function "checkDXL" in order to trap interpret errors. Let the code itself trap it's own run time errors. -Louie |
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial Mathias Mamsch - Wed Feb 20 07:00:52 EST 2013
By the way, if you want to avoid having to copy the contents of a buffer, that you pass to eval ( which can be relevant if you pass megabytes of data), then you can use can pass a reference to the eval_ by using a pointer. The problem is you need a static reassignable reference (=pointer) gBufPrintCode and put its static address to the eval code. Unfortunately you have to dereference twice in the eval code. Not nice, but fits its purpose ;-)
Buffer *gBufPrintCode
string sGlobalPrintCode = "
Buffer **bufEval = addr_ " ((addr_ (&gBufPrintCode)) int)"
Buffer &buf = **bufEval
print tempStringOf (buf)
"
void printString(Buffer &sPar) {
gBufPrintCode = &sPar
eval_ sGlobalPrintCode
}
Buffer buf = create(); buf = "Hello eval_ function!"
printString buf
Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
Hi Mathias, Your tutorial on pointers was extremely helpful. Unfortunately, I'm stuck with a situation in which I need to pass back data (specifically a Filter object) from an eval_ context, and I'm not sure how to do so. The examples above show how to pass data INTO an eval_ context, but I'm not sure if it's possible to go the other way.
I thought I could try passing in a pointer, updating the pointer in the eval_ context, and then deferencing it again back in the main context, but it seems that the pointer's address changes when I reassign it. I must be doing something silly, but can't figure out what exactly. Here is some simplified code without even using the eval_ context yet, that shows the pointer's address changing. // to be run from a module with a current filter defined Filter f1 = null Filter *ptr = &f1 print ((addr_ ptr) int) Filter f2 = current Filter *ptr_copy = addr_ ((addr_ ptr) int) print ((addr_ ptr_copy) int) ptr_copy = &f2 print ((addr_ ptr_copy) int) // filter *ptr is still null - I would like it to be updated to the value of f2 //print stringOf(current Module, *ptr) "\n"
This prints out:
336265320
Thanks! - Dave |
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial dave.k - Wed Aug 12 16:28:57 EDT 2015 Hi Mathias, Your tutorial on pointers was extremely helpful. Unfortunately, I'm stuck with a situation in which I need to pass back data (specifically a Filter object) from an eval_ context, and I'm not sure how to do so. The examples above show how to pass data INTO an eval_ context, but I'm not sure if it's possible to go the other way.
I thought I could try passing in a pointer, updating the pointer in the eval_ context, and then deferencing it again back in the main context, but it seems that the pointer's address changes when I reassign it. I must be doing something silly, but can't figure out what exactly. Here is some simplified code without even using the eval_ context yet, that shows the pointer's address changing. // to be run from a module with a current filter defined Filter f1 = null Filter *ptr = &f1 print ((addr_ ptr) int) Filter f2 = current Filter *ptr_copy = addr_ ((addr_ ptr) int) print ((addr_ ptr_copy) int) ptr_copy = &f2 print ((addr_ ptr_copy) int) // filter *ptr is still null - I would like it to be updated to the value of f2 //print stringOf(current Module, *ptr) "\n"
This prints out:
336265320
Thanks! - Dave Well this is really a pointer question and could go better in this thread: https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014683160&ps=25 Anyway - to modify a variable by a pointer you need to assign a *value* to the pointer. Not re-reference it. int f1 = 0 int f2 = 6 int *ptr = &f1 *ptr = 5 // will give a new value to the memory address that ptr is pointing to (f1) ptr = &f2 // will change the memory address that ptr points to to f2 Anyway I can not ensure that you will get no memory corruption when passing your Filter outside of the eval context. Chances are that some data structures inside the filter might be deallocated, when the eval ends, and the filter you assigned in your parent DXL context is invalid. So if you get crashes I would resort, to setting the Filter to the module inside the eval_ (current = f1) and reading it back from the module after the eval_ (f1 = current). This way, you can be sure, there is no problem with deallocation. Regards, Mathias |
Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial Mathias Mamsch - Wed Aug 12 17:11:41 EDT 2015 Well this is really a pointer question and could go better in this thread: https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014683160&ps=25 Anyway - to modify a variable by a pointer you need to assign a *value* to the pointer. Not re-reference it. int f1 = 0 int f2 = 6 int *ptr = &f1 *ptr = 5 // will give a new value to the memory address that ptr is pointing to (f1) ptr = &f2 // will change the memory address that ptr points to to f2 Anyway I can not ensure that you will get no memory corruption when passing your Filter outside of the eval context. Chances are that some data structures inside the filter might be deallocated, when the eval ends, and the filter you assigned in your parent DXL context is invalid. So if you get crashes I would resort, to setting the Filter to the module inside the eval_ (current = f1) and reading it back from the module after the eval_ (f1 = current). This way, you can be sure, there is no problem with deallocation. Regards, Mathias In your tutorial we see this:
No doubt where I got that notion for my other post. Now it seems the Current stuff is inherited by the eval_ context whereas in earlier releases of DOORS it was not. -Louie |